/*
 * Copyright (c) 2013 WDCY Information Technology Co. Ltd
 * www.wdcy.cc
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * WDCY Information Technology Co. Ltd ("Confidential Information").
 * You shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement you
 * entered into with WDCY Information Technology Co. Ltd.
 */
package com.monkey.web.platform.service.impl;

import com.monkey.core.domain.auth.UserRole;
import com.monkey.core.domain.auth.UserRoleRepository;
import com.monkey.core.domain.permission.Permission;
import com.monkey.core.domain.permission.PermissionRepository;
import com.monkey.core.domain.resource.Menu;
import com.monkey.core.domain.resource.MenuRepository;
import com.monkey.core.domain.role.Role;
import com.monkey.core.domain.role.RoleRepository;
import com.monkey.core.domain.role.RoleResourcePermission;
import com.monkey.core.domain.role.RoleResourcePermissionRepository;
import com.monkey.core.domain.user.User;
import com.monkey.core.exception.MonkeyException;
import com.monkey.core.plugin.dto.MonkeyDto;
import com.monkey.core.repository.search.SearchOperator;
import com.monkey.core.repository.search.Searchable;
import com.monkey.core.service.BaseServiceImpl;
import com.monkey.utils.MonkeyUtils;
import com.monkey.web.platform.exception.CanNotFoundPermissionException;
import com.monkey.web.platform.exception.CanNotFoundResourceException;
import com.monkey.web.platform.service.MenuService;
import com.monkey.web.platform.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author Wu Tianqiang
 */
@Service
public class RoleServiceImpl extends BaseServiceImpl<Role, Long> implements RoleService {


    @Autowired
    private MenuService menuService;
    @Autowired
    private MenuRepository menuRepository;
    @Autowired
    private RoleRepository roleRepository;
    @Autowired
    private PermissionRepository permissionRepository;
    @Autowired
    private UserRoleRepository userRoleRepository;
    @Autowired
    private RoleResourcePermissionRepository roleResourcePermissionRepository;

    @Override
    public void addRole(MonkeyDto monkeyDto) {
        Role role = Role.createFromMonkeyDto(monkeyDto);
        Searchable searchable1 = Searchable.newSearchable();
        searchable1.addSearchFilter("role", SearchOperator.eq, role.getRole());
        if (exist(searchable1)) {
            throw new MonkeyException("角色标识符已经存在");
        }

        Searchable searchable2 = Searchable.newSearchable();
        searchable2.addSearchFilter("name", SearchOperator.eq, role.getName());
        if (exist(searchable2)) {
            throw new MonkeyException("角色名已经存在");
        }
        save(role);
    }

    @Override
    public void modifyRole(MonkeyDto monkeyDto) {
        Role role = findOne(monkeyDto.getLong("id"));
        Role.modifyFromMonkeyDto(role, monkeyDto);

        Searchable searchable1 = Searchable.newSearchable();
        searchable1.addSearchFilter("id", SearchOperator.ne, role.getId());
        searchable1.addSearchFilter("role", SearchOperator.eq, role.getRole());
        if (exist(searchable1)) {
            throw new MonkeyException("角色标识符已经存在");
        }

        Searchable searchable2 = Searchable.newSearchable();
        searchable2.addSearchFilter("id", SearchOperator.ne, role.getId());
        searchable2.addSearchFilter("name", SearchOperator.eq, role.getName());
        if (exist(searchable2)) {
            throw new MonkeyException("角色名已经存在");
        }

        update(role);
    }


    private List<RoleResourcePermission> createRoleResourcePermissions(Role role, Map<Long, String> authorizations) {
        List<RoleResourcePermission> roleResourcePermissions = new ArrayList<>();
        Set<Long> keys = authorizations.keySet();
        for (Long key : keys) {
            String[] permissionIds = authorizations.get(key).split(RoleResourcePermission.DEFAULT_PERMISSIONS_SEPARATOR);
            for (String permissionId : permissionIds) {
                RoleResourcePermission roleResourcePermission = new RoleResourcePermission();
                roleResourcePermission.setRole(role);

                Menu menu = menuRepository.findOne(key);
                if (MonkeyUtils.isEmpty(menu)) {
                    throw new CanNotFoundResourceException(new Object[]{key});
                }
                roleResourcePermission.setMenu(menu);

                Permission permission = permissionRepository.findOne(Long.valueOf(permissionId));
                if (MonkeyUtils.isEmpty(permission)) {
                    throw new CanNotFoundPermissionException();
                }
                roleResourcePermission.setPermission(permission);

                roleResourcePermissions.add(roleResourcePermission);
            }
        }
        return roleResourcePermissions;
    }

    @Override
    public void grantRoleToUser(Long[] roleIds, User user) {
        for (Long roleId : roleIds) {
            UserRole userRole = new UserRole();
            userRole.setRole(roleRepository.findOne(roleId));
            userRole.setUser(user);
            userRoleRepository.save(userRole);
        }
    }

    @Override
    public void recoveryRoleFromUser(Long[] roleIds, Long userId) {
        for (Long roleId : roleIds) {
            userRoleRepository.deleteByUserIdAndRoleId(userId, roleId);
        }
    }

    @Override
    public Set<String> findStringRoles(Long userId) {
        Set<String> roles = new HashSet<>();
        List<UserRole> userRoles = userRoleRepository.findByUserId(userId);
        for (UserRole userRole : userRoles) {
            Role role = userRole.getRole();
            roles.add(role.getRole());
        }
        return roles;
    }

    @Override
    public Set<String> resolvePermissionsInRole(String roleString) {
        Set<RoleResourcePermission> roleResourcePermissions = roleResourcePermissionRepository.findByRoleString(roleString);
        Set<String> permissions = new HashSet<>();
        for (RoleResourcePermission roleResourcePermission : roleResourcePermissions) {
            Menu menu = roleResourcePermission.getMenu();
            Permission permission = roleResourcePermission.getPermission();
            String actualResourceIdentity = menuService.findActualMenuIdentity(menu);
            permissions.add(actualResourceIdentity + Permission.PART_DIVIDER_TOKEN + permission.getPermission());
        }
        return permissions;
    }

    @Override
    public void grantPermissionToRole(Role role, Menu menu, Long[] permissionIds) {
        for (Long permissionId : permissionIds) {
            RoleResourcePermission rrp = new RoleResourcePermission();
            rrp.setRole(role);
            rrp.setMenu(menu);
            Permission permission = permissionRepository.findOne(permissionId);
            rrp.setPermission(permission);
            roleResourcePermissionRepository.save(rrp);
        }
    }

    @Override
    public void recoveryPermissionFromRole(Role role, Menu menu, Long[] permissionIds) {
        for (Long permissionId : permissionIds) {
            Permission permission = permissionRepository.findOne(permissionId);
            roleResourcePermissionRepository.deleteByRoleAndMenuAndPermission(role, menu, permission);
        }
    }

    @Override
    public List<Permission> getPermissionByRoleMenu(Role role, Menu menu) {
        List<RoleResourcePermission> rrps = roleResourcePermissionRepository.findByRoleAndMenu(role, menu);
        List<Permission> permissions = new ArrayList<>();
        for (RoleResourcePermission rrp : rrps) {
            permissions.add(rrp.getPermission());
        }
        return permissions;
    }

    @Override
    public Page<RoleResourcePermission> getPermissionByRole(Role role, Pageable pageable) {
        Page<RoleResourcePermission> rrps = roleResourcePermissionRepository.findByRole(role, pageable);
        for (RoleResourcePermission rrp : rrps.getContent()) {
            String actualMenuIdentity = menuService.findActualMenuIdentity(rrp.getMenu());
            rrp.setActualIdentity(actualMenuIdentity + ":" + rrp.getPermission().getPermission());
        }
        return rrps;
    }

}